import React from "react";
import { Button, Input, Toast, DotLoading } from "antd-mobile";
import { fetchSSE } from "./utils";
// import * as marked from "marked";
import { marked as Marked } from "marked";
import hljs from "highlight.js";
import { markedHighlight } from "marked-highlight";
import "highlight.js/styles/github.css";
import "./index.less";

const SERVER = "SERVER";
const SERVER_LOADING = "SERVER_LOADING";
const CLIENT = "CLIENT";
class Chat extends React.PureComponent {
  constructor(props) {
    super(props);
    this.state = {
      inputValue: "",
      isSending: false,
      messageList: [
        {
          type: SERVER,
          message: "你好，有啥问题都可以和我聊哟",
        },
      ],
    };

    this.eventSource;
    this.marked;
    this.chatBodyRef = React.createRef();
    this.scrollTimer;
  }

  componentDidMount() {
    this.initMarked();
  }

  componentWillUnmount() {
    this.eventSource && this.eventSource();
  }

  initMarked = () => {
    this.marked = Marked;
    this.marked.use(
      markedHighlight({
        highlight: (code, language) => {
          const isValidLang = hljs.getLanguage(language)
            ? language
            : "plaintext";
          return hljs.highlight(code, { language: isValidLang }).value;
        },
      })
    );
  };

  /**
   * 输入框值改变
   */
  handleOnInputValueChange = (value) => {
    this.setState({ inputValue: value });
  };

  /**
   * 格式化消息
   */
  handleFormatMessage = (message) => {
    if (!message) {
      return;
    }
    const htmlText = this.marked.parse(message);
    return htmlText;
  };

  /**
   * 添加信息
   * @param {string} message 消息
   * @param {string} type 类型， SERVER 服务端消息，CLIENT 客户端消息
   */
  addMessage = (message, type) => {
    const { messageList } = this.state;
    messageList.push({
      key: Math.random(),
      type,
      message,
    });

    this.setState({
      messageList: [...messageList],
    });
  };

  /**
   * 添加信息
   * @param {Object} messageItem 消息项
   * @param {string} key 消息 key
   */
  updateMessage = (messageItem, key) => {
    if (!messageItem || !key) {
      return;
    }
    const { messageList } = this.state;
    for (let i = 0; i < messageList.length; i++) {
      if (messageList[i].key !== key) {
        continue;
      }
      messageList[i] = Object.assign(messageList[i], messageItem);
    }
    this.setState({ messageList: [...messageList] });
  };

  /**
   * 连接关闭或者异常回调
   * @param {Object} res 响应
   */
  eventSourceErrorOrClosedHandler = (res) => {
    if (res?.type === "error") {
      Toast.show({
        content: "请求异常",
      });
      console.error(res.error);
      const { messageList } = this.state;
      const messageLength = messageList?.length;
      const lastMessage = messageList[messageLength - 1];
      if (messageLength && lastMessage.type === SERVER_LOADING) {
        lastMessage.message = "网络出现了问题，请重试";
        this.updateMessage(lastMessage, lastMessage.key);
      }
    }
  };

  /**
   * 连接关闭或者异常回调
   * @param {Object} res 响应
   */
  eventSourceFinishedHandler = (res) => {
    this.setState({ isSending: false });
  };

  /**
   * 判断聊天框是否滚到了底部
   */
  isChatBodyScrollAtBottom = () => {
    const { scrollHeight, scrollTop, offsetHeight } = this.chatBodyRef?.current;

    if (scrollHeight - (scrollTop + offsetHeight) < 30) {
      return true;
    }

    return false;
  };

  /**
   * 滚动到消息底部
   */
  chatMessageScrollToBottom = () => {
    clearTimeout(this.scrollTimer);

    this.scrollTimer = setTimeout(() => {
      this.chatBodyRef?.current.scroll(0, 9999999);
    }, 100);
  };

  /**
   * 处理请求消息
   * @param {Object} res 响应消息对象
   */
  eventSourceOnMessage = (res) => {
    let { text } = res?.data?.output || {};
    text = text?.trim();
    if (!text) {
      return;
    }
    const formatMessage = this.handleFormatMessage(text);
    if (!formatMessage) {
      return;
    }
    // 添加服务端消息
    const { messageList } = this.state;
    const { type } = messageList[messageList.length - 1] || {};
    if (!type || type === CLIENT) {
      this.addMessage(formatMessage, SERVER);
    } else {
      this.updateMessage(
        { message: formatMessage, type: SERVER },
        messageList[messageList.length - 1]?.key
      );
    }

    if (this.isChatBodyScrollAtBottom()) {
      // 消息内容增加时，需要滚动到最新消息处
      this.chatMessageScrollToBottom();
    }
  };

  /**
   * 清除输入框值
   */
  clearInputValue = () => {
    this.setState({ inputValue: "" });
  };

  /**
   * 发送消息
   */
  handleSendOrStop = async () => {
    const { inputValue, isSending } = this.state;

    if (isSending && this.eventSource) {
      // 发送消息状态下，停止请求
      this.eventSource();
      return;
    }

    // 发送消息
    if (!inputValue || !inputValue.trim()) {
      return;
    }

    // 滚动到消息底部
    this.chatMessageScrollToBottom();

    // 清除输入框值
    this.clearInputValue();

    // 添加客户端消息
    this.addMessage(inputValue, CLIENT);

    // 添加加载中服务端消息
    this.addMessage("", SERVER_LOADING);

    // TODO: 测试
    // this.addMessage(
    //   this.handleFormatMessage(
    //     'JavaScript (JS) 是一种广泛用于网页和服务器的编程语言，主要用于实现客户端的网页脚本。以下是一些常见的JS示例，涵盖了基础操作到一些高级应用的概念。\n\n### 1. 输出 "Hello, World!"\n\n```javascript\nconsole.log("Hello, World!");\n```\n\n### 2. 变量声明与使用\n\n```javascript\nlet age = 25; // 使用let声明变量\nconst pi = 3.14; // 使用const声明常量\n\nage = 26; // 变量值可以改变\n// pi = 3; // 这会报错，因为const声明的变量不可更改\n\nconsole.log(age);\n```\n\n### 3. 条件语句\n\n```javascript\nlet score = 85;\n\nif (score >= 90) {\n    console.log("优秀");\n} else if (score >= 70) {\n    console.log("良好");\n} else {\n    console.log("加油！");\n}\n```\n\n### 4. 循环\n\n#### for循环\n\n```javascript\nfor (let i = 0; i < 5; i++) {\n    console.log(i);\n}\n```\n\n#### while循环\n\n```javascript\nlet count = 0;\nwhile (count < 3) {\n    console.log(count);\n    count++;\n}\n```\n\n### 5. 函数定义与调用\n\n```javascript\nfunction addNumbers(num1, num2) {\n    return num1 + num2;\n}\n\nlet result = addNumbers(5, 3);\nconsole.log(result); // 输出8\n```\n\n### 6. 数组操作\n\n```javascript\nlet fruits = ["苹果", "香蕉", "橙子"];\n\n// 添加元素\nfruits.push("葡萄");\n\n// 删除最后一个元素\nfruits.pop();\n\n// 遍历数组\nfruits.forEach(function(fruit) {\n    console.log(fruit);\n});\n```\n\n### 7. 对象操作\n\n```javascript\nlet person = {\n    name: "张三",\n    age: 30,\n    sayHello: function() {\n        console.log("你好，我是" + this.name);\n    }\n};\n\n// 访问属性\nconsole.log(person.age);\n\n// 调用方法\nperson.sayHello();\n```\n\n### 8. AJAX请求\n\n使用Fetch API进行异步HTTP请求：\n\n```javascript\nfetch(\'https://api.example.com/data\')\n    .then(response => response.json())\n    .then(data => console.log(data))\n    .catch(error => console.error(\'Error:\', error));\n```\n\n### 9. ES6箭头函数\n\n```javascript\nconst multiply = (x, y) => x * y;\nconsole.log(multiply(4, 5)); // 输出20\n```\n\n### 10. 解构赋值\n\n```javascript\nlet [a, b] = [1, 2];\nconsole.log(a); // 输出1\nconsole.log(b); // 输出2\n```\n\n这些示例覆盖了JavaScript编程中的一些基础概念，从基本的输出、变量声明到更复杂的函数、对象操作以及异步请求。希望它们能帮助你更好地理解JavaScript的使用。'
    //   ),
    //   SERVER
    // );
    // return;

    // 显示 loading 效果
    this.setState({
      isSending: true,
      messageList: [...this.state.messageList],
    });

    // 标记用户没有主动滚动，用于判断是否有程序自动滚动到最新消息处
    this.isUserScroll = false;

    // 请求参数
    const params = {
      model: "qwen-max",
      input: {
        messages: [
          {
            role: "system",
            content: "You are a helpful assistant.",
          },
          {
            role: "user",
            content: this.state.inputValue,
          },
        ],
      },
      parameters: {},
    };
    // 请求头
    const headers = {
      Accept: "text/event-stream",
      "Content-Type": "application/json",
      "X-Response-Stream": "true",
      "X-DashScope-SSE": "enable",
    };
    // 发起请求
    this.eventSource = await fetchSSE("/sse", params, {
      headers,
      onMessage: this.eventSourceOnMessage,
      onClose: this.eventSourceErrorOrClosedHandler,
      onError: this.eventSourceErrorOrClosedHandler,
      onFinished: this.eventSourceFinishedHandler,
    });
  };

  /**
   * 按 Enter 键发送消息
   */
  handleOnInputKeyUp = (e) => {
    if (e.key === "Enter") {
      this.handleSendOrStop();
    }
  };

  /**
   * 渲染消息项
   * @param {Object} messageItem 消息项
   */
  renderMessageItem = (messageItem) => {
    if (!messageItem) {
      return;
    }

    const className = [SERVER, SERVER_LOADING].includes(messageItem.type)
      ? "baby-love-chat-item-server"
      : "baby-love-chat-item-client";
    const isSending = messageItem.type === SERVER_LOADING;
    return (
      <li key={messageItem.key} className={className}>
        {isSending ? (
          <div className="baby-love-chat-item-content">
            <DotLoading color="primary" />
          </div>
        ) : (
          <div
            className="baby-love-chat-item-content"
            dangerouslySetInnerHTML={{ __html: messageItem.message }}
          ></div>
        )}
      </li>
    );
  };

  render() {
    const { messageList, inputValue, isSending } = this.state;

    return (
      <div className="baby-love-chat">
        <ul ref={this.chatBodyRef} className="baby-love-chat-body">
          {messageList.map((message) => this.renderMessageItem(message))}
        </ul>
        <footer className="baby-love-chat-footer">
          <Input
            placeholder="请输入内容"
            value={inputValue}
            disabled={isSending}
            onChange={this.handleOnInputValueChange}
            onKeyUp={this.handleOnInputKeyUp}
          />
          <Button size="mini" color="primary" onClick={this.handleSendOrStop}>
            {isSending ? "停止" : "发送"}
          </Button>
        </footer>
      </div>
    );
  }
}

export default Chat;
